import sys
import os
import logging
from pathlib import Path
from typing import Optional, List, Dict
import pandas as pd
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
    QPushButton, QLabel, QLineEdit, QFileDialog, QComboBox, 
    QProgressBar, QTextEdit, QMessageBox, QCheckBox, QGroupBox
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QSize

from crash_parser import CrashParser

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("crashparser.log"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger("GUI")

class ParserWorker(QThread):
    """解析工作线程"""
    progress_updated = pyqtSignal(int, int)
    log_message = pyqtSignal(str)
    finished_with_result = pyqtSignal(object)
    error_occurred = pyqtSignal(str)
    
    def __init__(self, dsym_path: str, file_path: str, file_type: str, 
                 output_path: Optional[str] = None, stack_column: Optional[str] = None,
                 result_column: str = "result", arch: str = "arm64", 
                 encoding: Optional[str] = None):
        super().__init__()
        self.dsym_path = dsym_path
        self.file_path = file_path
        self.file_type = file_type  # 'csv' 或 'txt'
        self.output_path = output_path
        self.stack_column = stack_column
        self.result_column = result_column
        self.arch = arch
        self.encoding = encoding
        self.result = None
    
    def run(self):
        try:
            self.log_message.emit(f"开始解析崩溃堆栈，使用dSYM路径: {self.dsym_path}")
            self.log_message.emit(f"输入文件: {self.file_path}, 类型: {self.file_type}")
            
            if self.encoding:
                self.log_message.emit(f"使用指定编码: {self.encoding}")
            
            # 创建解析器
            parser = CrashParser(self.dsym_path, self.arch)
            
            # 解析文件
            if self.file_type.lower() == 'csv':
                self.log_message.emit("解析CSV文件...")
                result = parser.parse_csv_file(
                    csv_file=self.file_path,
                    stack_column=self.stack_column,
                    output_file=self.output_path,
                    result_column=self.result_column,
                    encoding=self.encoding
                )
                self.result = result
                
            elif self.file_type.lower() == 'txt':
                self.log_message.emit("解析TXT文件...")
                result = parser.parse_txt_file(
                    txt_file=self.file_path,
                    output_file=self.output_path,
                    encoding=self.encoding
                )
                self.result = result
            
            self.log_message.emit("解析完成!")
            self.finished_with_result.emit(self.result)
            
        except Exception as e:
            error_msg = f"解析过程中出错: {str(e)}"
            logger.error(error_msg)
            self.error_occurred.emit(error_msg)


class CrashParserGUI(QMainWindow):
    """崩溃堆栈解析工具GUI"""
    
    def __init__(self):
        super().__init__()
        # 修复初始化顺序问题，先初始化log_buffer
        self.log_buffer = []
        self.parser_worker = None
        self.init_ui()
    
    def init_ui(self):
        """初始化UI"""
        self.setWindowTitle("iOS崩溃堆栈解析工具")
        self.setMinimumSize(800, 600)
        
        # 主布局
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout(main_widget)
        
        # 文件选择区域
        file_group = QGroupBox("文件选择")
        file_layout = QVBoxLayout(file_group)
        
        # 输入文件选择
        input_layout = QHBoxLayout()
        input_label = QLabel("崩溃堆栈文件:")
        self.input_file_path = QLineEdit()
        self.input_file_path.setReadOnly(True)
        input_browse = QPushButton("浏览")
        input_browse.clicked.connect(self.browse_input_file)
        
        input_layout.addWidget(input_label)
        input_layout.addWidget(self.input_file_path, 1)
        input_layout.addWidget(input_browse)
        file_layout.addLayout(input_layout)
        
        # 文件类型选择
        file_type_layout = QHBoxLayout()
        file_type_label = QLabel("文件类型:")
        self.file_type_combo = QComboBox()
        self.file_type_combo.addItems(["CSV", "TXT"])
        
        file_type_layout.addWidget(file_type_label)
        file_type_layout.addWidget(self.file_type_combo)
        
        # 编码选择
        encoding_label = QLabel("文件编码:")
        self.encoding_combo = QComboBox()
        self.encoding_combo.addItems(["自动检测", "UTF-8", "UTF-8-SIG", "GBK", "GB2312", "ISO-8859-1", "CP1252"])
        self.encoding_combo.setCurrentIndex(0)
        
        file_type_layout.addWidget(encoding_label)
        file_type_layout.addWidget(self.encoding_combo)
        file_type_layout.addStretch(1)
        file_layout.addLayout(file_type_layout)
        
        # CSV特定选项
        self.csv_options_group = QGroupBox("CSV选项")
        csv_options_layout = QHBoxLayout(self.csv_options_group)
        
        column_label = QLabel("堆栈列名:")
        self.stack_column = QLineEdit()
        self.stack_column.setPlaceholderText("自动检测")
        
        result_column_label = QLabel("结果列名:")
        self.result_column = QLineEdit("result")
        
        csv_options_layout.addWidget(column_label)
        csv_options_layout.addWidget(self.stack_column)
        csv_options_layout.addWidget(result_column_label)
        csv_options_layout.addWidget(self.result_column)
        
        file_layout.addWidget(self.csv_options_group)
        
        # dSYM文件选择
        dsym_layout = QHBoxLayout()
        dsym_label = QLabel("dSYM文件/目录:")
        self.dsym_path = QLineEdit()
        self.dsym_path.setReadOnly(True)
        dsym_browse = QPushButton("浏览")
        dsym_browse.clicked.connect(self.browse_dsym)
        
        dsym_layout.addWidget(dsym_label)
        dsym_layout.addWidget(self.dsym_path, 1)
        dsym_layout.addWidget(dsym_browse)
        file_layout.addLayout(dsym_layout)
        
        # 架构选择
        arch_layout = QHBoxLayout()
        arch_label = QLabel("目标架构:")
        self.arch_combo = QComboBox()
        self.arch_combo.addItems(["arm64", "armv7", "x86_64"])
        
        arch_layout.addWidget(arch_label)
        arch_layout.addWidget(self.arch_combo)
        arch_layout.addStretch(1)
        file_layout.addLayout(arch_layout)
        
        # 输出文件选择
        output_layout = QHBoxLayout()
        output_label = QLabel("输出文件:")
        self.output_file_path = QLineEdit()
        self.output_file_path.setReadOnly(True)
        output_browse = QPushButton("浏览")
        output_browse.clicked.connect(self.browse_output_file)
        
        output_layout.addWidget(output_label)
        output_layout.addWidget(self.output_file_path, 1)
        output_layout.addWidget(output_browse)
        file_layout.addLayout(output_layout)
        
        main_layout.addWidget(file_group)
        
        # 文件类型变化时更新UI
        self.file_type_combo.currentTextChanged.connect(self.update_ui_for_file_type)
        
        # 操作区域
        action_layout = QHBoxLayout()
        self.parse_button = QPushButton("开始解析")
        self.parse_button.clicked.connect(self.start_parsing)
        self.parse_button.setMinimumHeight(40)
        
        action_layout.addStretch(1)
        action_layout.addWidget(self.parse_button)
        action_layout.addStretch(1)
        
        main_layout.addLayout(action_layout)
        
        # 进度条
        progress_layout = QHBoxLayout()
        progress_label = QLabel("解析进度:")
        self.progress_bar = QProgressBar()
        
        progress_layout.addWidget(progress_label)
        progress_layout.addWidget(self.progress_bar, 1)
        
        main_layout.addLayout(progress_layout)
        
        # 日志区域
        log_group = QGroupBox("日志信息")
        log_layout = QVBoxLayout(log_group)
        
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        
        log_layout.addWidget(self.log_text)
        
        main_layout.addWidget(log_group, 1)
        
        # 初始化UI状态
        self.update_ui_for_file_type(self.file_type_combo.currentText())
        
        # 添加开始信息到日志
        self.add_log("iOS崩溃堆栈解析工具已启动，请选择文件")
    
    def update_ui_for_file_type(self, file_type: str):
        """根据文件类型更新UI"""
        if file_type.lower() == 'csv':
            self.csv_options_group.setVisible(True)
        else:
            self.csv_options_group.setVisible(False)
    
    def browse_input_file(self):
        """浏览崩溃堆栈文件"""
        file_type = self.file_type_combo.currentText()
        if file_type.lower() == 'csv':
            file_filter = "CSV文件 (*.csv)"
        else:
            file_filter = "文本文件 (*.txt)"
        
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择崩溃堆栈文件", "", file_filter
        )
        
        if file_path:
            self.input_file_path.setText(file_path)
            
            # 自动设置输出文件路径
            output_dir = os.path.dirname(file_path)
            file_name = os.path.basename(file_path)
            name, ext = os.path.splitext(file_name)
            
            output_path = os.path.join(output_dir, f"{name}_parsed{ext}")
            self.output_file_path.setText(output_path)
            
            self.add_log(f"已选择崩溃堆栈文件: {file_path}")
    
    def browse_dsym(self):
        """浏览dSYM文件或目录"""
        dsym_path = QFileDialog.getExistingDirectory(
            self, "选择dSYM文件或目录", "", QFileDialog.Option.ShowDirsOnly
        )
        
        if dsym_path:
            self.dsym_path.setText(dsym_path)
            self.add_log(f"已选择dSYM路径: {dsym_path}")
    
    def browse_output_file(self):
        """浏览输出文件保存位置"""
        file_type = self.file_type_combo.currentText()
        if file_type.lower() == 'csv':
            file_filter = "CSV文件 (*.csv)"
        else:
            file_filter = "文本文件 (*.txt)"
        
        output_path, _ = QFileDialog.getSaveFileName(
            self, "选择保存位置", self.output_file_path.text(), file_filter
        )
        
        if output_path:
            self.output_file_path.setText(output_path)
            self.add_log(f"已选择输出文件: {output_path}")
    
    def start_parsing(self):
        """开始解析崩溃堆栈"""
        # 检查输入
        dsym_path = self.dsym_path.text()
        if not dsym_path:
            QMessageBox.warning(self, "错误", "请选择dSYM文件或目录")
            return
        
        input_file = self.input_file_path.text()
        if not input_file:
            QMessageBox.warning(self, "错误", "请选择崩溃堆栈文件")
            return
        
        output_file = self.output_file_path.text()
        
        # 获取编码设置
        encoding_idx = self.encoding_combo.currentIndex()
        encoding = None if encoding_idx == 0 else self.encoding_combo.currentText()
        
        # 创建并启动工作线程
        file_type = self.file_type_combo.currentText().lower()
        stack_column = self.stack_column.text() if self.stack_column.text() else None
        result_column = self.result_column.text() if self.result_column.text() else "result"
        arch = self.arch_combo.currentText()
        
        self.parser_worker = ParserWorker(
            dsym_path=dsym_path,
            file_path=input_file,
            file_type=file_type,
            output_path=output_file,
            stack_column=stack_column,
            result_column=result_column,
            arch=arch,
            encoding=encoding
        )
        
        # 连接信号
        self.parser_worker.progress_updated.connect(self.update_progress)
        self.parser_worker.log_message.connect(self.add_log)
        self.parser_worker.finished_with_result.connect(self.parsing_finished)
        self.parser_worker.error_occurred.connect(self.parsing_error)
        
        # 更新UI状态
        self.parse_button.setEnabled(False)
        self.progress_bar.setValue(0)
        self.add_log("开始解析...")
        
        # 启动工作线程
        self.parser_worker.start()
    
    def update_progress(self, current: int, total: int):
        """更新进度条"""
        if total > 0:
            progress = int(current / total * 100)
            self.progress_bar.setValue(progress)
    
    def add_log(self, message: str):
        """添加日志消息"""
        self.log_buffer.append(message)
        self.log_text.append(message)
        
        # 滚动到底部
        scrollbar = self.log_text.verticalScrollBar()
        scrollbar.setValue(scrollbar.maximum())
        
        # 同时记录到应用日志
        logger.info(message)
    
    def parsing_finished(self, result):
        """解析完成处理"""
        self.parse_button.setEnabled(True)
        self.progress_bar.setValue(100)
        
        # 显示成功消息
        if result is not None:
            if isinstance(result, pd.DataFrame):
                rows = len(result)
            elif isinstance(result, list):
                rows = len(result)
            else:
                rows = 0
            
            success_msg = f"解析完成，共处理 {rows} 行数据"
            self.add_log(success_msg)
            
            if self.output_file_path.text():
                self.add_log(f"结果已保存到: {self.output_file_path.text()}")
                
                # 询问是否打开结果文件
                reply = QMessageBox.question(
                    self, "解析完成", f"{success_msg}\n是否打开结果文件?",
                    QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
                )
                
                if reply == QMessageBox.StandardButton.Yes:
                    # 使用系统默认程序打开文件
                    import subprocess
                    import platform
                    
                    try:
                        if platform.system() == 'Darwin':  # macOS
                            subprocess.call(('open', self.output_file_path.text()))
                        elif platform.system() == 'Windows':
                            os.startfile(self.output_file_path.text())
                        else:  # Linux
                            subprocess.call(('xdg-open', self.output_file_path.text()))
                    except Exception as e:
                        self.add_log(f"打开文件时出错: {str(e)}")
            else:
                QMessageBox.information(self, "解析完成", success_msg)
    
    def parsing_error(self, error_message: str):
        """解析出错处理"""
        self.parse_button.setEnabled(True)
        self.progress_bar.setValue(0)
        
        self.add_log(f"错误: {error_message}")
        QMessageBox.critical(self, "解析错误", error_message)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = CrashParserGUI()
    window.show()
    sys.exit(app.exec()) 